"""
场景组织
"""
import math
import numpy as np
from .type_enum import (
    PrimitiveType,
    MaterialType,
    LightType
)

from .gmath import (
    identity_mat4,
    rotate_mat4,
    translate_mat4,
    normalized
)
    
class Renderable:
    """
    可以被渲染的对象
    render_object = Renderable(build.build_box(10,10,10))
    render_object.rotate(pi/2,0,0,1)
    render_object.translate(10,0,0)
    render_object.add_child(ball)
    """
    def __init__(self,mesh=None,name=None):
        """
        skybox该对象是是一个天空盒子
        """
        self.mat4:np.ndarray = identity_mat4()
        self.childs:list = []
        self.mesh:dict = mesh
        self.light = None
        self.name = name
        self.material = (1.,1.,0.,1.,0.)

    def identity(self):
        """
        初始化矩阵
        """
        self.mat4 = identity_mat4()

    def translate(self,dx,dy,dz):
        """
        平移对象
        """
        tm = translate_mat4(dx,dy,dz)
        self.mat4 = tm @ self.mat4

    def rotate(self,angle,x,y,z):
        """
        绕向量x,y,z旋转angle
        """
        c = math.cos(angle)
        s = math.sin(angle)
        rm = rotate_mat4(c,s,x,y,z)
        self.mat4 = rm @ self.mat4

    def set_material(self,
                     t=MaterialType.BRDF,
                     c=(1.,1.,1.),
                     a=1.,
                     f=(0.,0.,0.),
                     metallic=0.,
                     mu=1.,
                     texture=None):
        """
        c 材质颜色
        a 表面粗糙度0-1
        f 菲涅尔参数
        metallic 金属性0-1
        """
        f_vec = f
        if isinstance(f,float) or isinstance(f,int):
            f_vec = (f,f,f)
        self.material = {
            "type" :    t,
            "color" :   c,
            "a" :       a,
            "f" :       f_vec,
            "metallic": metallic,
            "mu":       mu,
            "texture" : texture
        }

    def scale(self,x,y,z):
        """
        缩放对象
        """
        sm = identity_mat4()
        sm[0,0] = x
        sm[1,1] = y
        sm[2,2] = z
        self.mat4 = sm @ self.mat4

    def add_child(self,child):
        """
        加入子节点
        """
        self.childs.append(child)

    def for_each_child(self,m:np.ndarray,cb):
        """
        遍历全部对象
        """
        mat4 = m @ self.mat4
        cb(mat4,self)
        for o in self.childs:
            o.for_each_child(mat4,cb)

class Light(Renderable):
    def __init__(self,t,
                  name = None,
                  mesh = None,
                  p = (0.,0.,0.),
                  power=0.,
                  color=(1.,1.,1.),
                  d=(0.,0.,-1.),
                  angle=0.,
                  texture=None):
        super().__init__(mesh=mesh,name=name)
        self.light = {
            "type":t,
            "p":p,
            "color":color,
            "power":power,
            "dir":d,
            "angle":angle,
            "texture":texture
        }

class Camera:
    """
    摄像机 , 眼睛在原点面向-z,上方为+y,右为+x
    """
    def __init__(self,pos=None,look_pos=None):
        self.mat4 = identity_mat4()
        self.eye_pos = np.array(pos)
        self.look_pos = np.array(look_pos)
        if pos is not None and look_pos is not None:
            self.look_at(pos,look_pos)

    def look_at(self,pos:np.ndarray,look_pos:np.ndarray):
        """
        重新设置摄像机位置(pos)和观察位置(look_pos)
        """
        e = normalized(pos-look_pos)
        bl = np.sqrt(np.sum(e[:2]*e[:2]))
        if bl==0:
            bl = 1
        cos1 = e[0]/bl #alpha
        sin1 = e[1]/bl
        cos2 = e[2]    #theta
        sin2 = bl
        m = rotate_mat4(cos2,sin2,1.,0.,0.)
        m = rotate_mat4(-sin1,cos1,0.,0.,1.) @ m
        m = translate_mat4(pos[0],pos[1],pos[2]) @ m
        self.mat4 = np.linalg.inv(m)

    def move_wsad(self,dx:float,dy:float,dz:float=0.):
        """
        前后移动dy,水平移动dx
        """
        self.mat4 = translate_mat4(-dx,-dz,-dy) @ self.mat4

    def rotate_fov(self,heading:float,pitch:float):
        """
        heading绕y,pitch绕x
        """
        sin = math.sin
        cos = math.cos
        self.mat4 = rotate_mat4(cos(pitch),sin(pitch),1,0,0) @ self.mat4
        a = self.mat4 @ np.array([0,0,0,1])
        b = self.mat4 @ np.array([0,0,1,1])
        n = normalized((b-a)[:3])
        self.mat4 = rotate_mat4(cos(heading),sin(heading),n[0],n[1],n[2]) @ self.mat4

    def rotate_look_pos(self,angle:float):
        """
        绕观察点旋转
        """
        inv_mat4 = np.linalg.inv(self.mat4)
        eye_pos = inv_mat4 @ np.array([0.,0.,0.,1.])
        tm0 = translate_mat4(-self.look_pos[0],-self.look_pos[1],-self.look_pos[2])
        rm = rotate_mat4(math.cos(angle),math.sin(angle),0.,0.,1.)
        tm1 = translate_mat4(self.look_pos[0],self.look_pos[1],self.look_pos[2])
        self.look_at((tm1 @ rm @ tm0 @ eye_pos)[:3],self.look_pos)

class FilmCamera(Camera):
    """
    具有景深功能
    """
    def __init__(self,l,phi,pos=None,look_pos=None,f=0.1):
        super().__init__(pos,look_pos)
        self.lens_l = l
        self.lens_phi = f/phi
        self.lens_f = f

    def get_lens(self):
        """
        取得物距和光圈
        """
        return self.lens_l,self.lens_phi
    
class Scenes:
    """
    组织场景结构
    """
    def __init__(self):
        self.objs:Renderable = []
        self.camera = None
        self.skybox = None
        self.environment_color = [0.5,0.5,0.5]

    def set_environment_color(self,c):
        """
        设置环境色
        """
        self.environment_color = c

    def add_renderable(self,obj:Renderable):
        """
        向场景中加入一个可渲染对象
        """
        self.objs.append(obj)

    def set_camera(self,cam:Camera):
        """
        设置当前摄像机
        """
        self.camera = cam

    def get_camera(self):
        return self.camera

    def for_each(self,cb,mat=None):
        """
        遍历全部对象
        """
        if mat is None:
            mat4 = self.camera.mat4 if self.camera is not None else identity_mat4()
        else:
            mat4 = mat
        for o in self.objs:
            o.for_each_child(mat4,cb)

    def update(self,dt:float):
        """
        更新函数 dt等于两次更新的时间差单位秒
        """
